<!DOCTYPE html>
<meta charset="UTF-8">
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id="target"></div>
<script>
// These tests target post multiplication interpolation behaviour of the transform property.
// https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms

function assertPostMultiplication(start, end) {
  test(() => {
    assert_true(interpolationUsesPostMultiplication(start, end));
  }, 'Animating transform from ' + start + ' to ' + end + ' should use post multiplication');
}

function assertNoPostMultiplication(start, end) {
  test(() => {
    assert_false(interpolationUsesPostMultiplication(start, end));
  }, 'Animating transform from ' + start + ' to ' + end + ' should not use post multiplication');
}

function interpolationUsesPostMultiplication(start, end) {
    // This value suffix will cause post multiplication to interpolate differently than pairwise
    // interpolation. Rotating by 370 degrees is equivalent to rotating by 10 degrees in the  matrix
    // representation.
    var testStart = start + ' rotate(370deg)';
    var testEnd = end + ' rotate(0deg)';

    var animation = target.animate({transform: [toMatrix(testStart), toMatrix(testEnd)]}, 1);
    animation.currentTime = 0.5;
    var postMultipliedInterpolation = getComputedStyle(target).transform;
    animation.cancel();

    animation = target.animate({transform: [testStart, testEnd]}, 1);
    animation.currentTime = 0.5;
    var interpolation = getComputedStyle(target).transform;
    animation.cancel();

    return matricesApproxEqual(interpolation, postMultipliedInterpolation, 0.01);
}

function toMatrix(transform) {
  target.style.transform = transform;
  var matrix = getComputedStyle(target).transform;
  target.style.transform = '';
  return matrix;
}

function matricesApproxEqual(actualMatrix, expectedMatrix, epsilon) {
  var extractmatrix = function(matrixStr) {
    var list = [];
    var regex = /[+\-]?[0-9]+[.]?[0-9]*(e[+/-][0-9]+)?/g;
    var match = undefined;
    do {
      match = regex.exec(matrixStr);
      if (match) {
        list.push(parseFloat(match[0]));
      }
    } while (match);
    return list;
  }
  var actualNumbers = extractmatrix(actualMatrix);
  var expectedNumbers = extractmatrix(expectedMatrix);
  if (actualNumbers.length !== expectedNumbers.length) {
    return false;
  }
  for (var i = 0; i < actualNumbers.length; i++) {
    if (Math.abs(actualNumbers[i] - expectedNumbers[i]) > epsilon) {
      return false;
    }
  }
  return true;
}

// Verify that the transform prefix generates different interpolations based on matrix and pairwise
// interpolation.
assertNoPostMultiplication('', '');

// Transforms are not of the same type: fallback to 4x4 matrix.
assertPostMultiplication('translateX(10px)', 'matrix(1, 1, 0, 1, 1, 1)');
assertPostMultiplication('translateX(10px)', 'rotate(90deg)');
assertPostMultiplication('skewX(45deg)', 'translateX(10px)');
assertPostMultiplication(
    'matrix(1, 0, 0, 1, 0, 0)',
    'matrix3d(2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)');

// TODO(kevers): Investigate if skewX(a) and skewY(b) should map to skew(a, 0) and skew(0, b),
// respectively. If correct, then pairwise interpolation should be used.
assertPostMultiplication('skewX(15deg)', 'skewY(15deg)');
assertPostMultiplication('skew(15deg, 0deg)', 'skewY(15deg)');

// Transform pairs can be expressed in the form of a common primitive.
assertNoPostMultiplication('translate(10px, 20px)', 'translateX(100px)');
assertNoPostMultiplication('translateX(10px)', 'translateY(100px)');
assertNoPostMultiplication('translateX(10px)', 'translateZ(100px)');
assertNoPostMultiplication('translateZ(10px)', 'translate3d(100px, 20px, 30px)');
assertNoPostMultiplication('scaleX(2)', 'scaleY(1)');
assertNoPostMultiplication('scaleX(2)', 'scaleZ(1)');
assertNoPostMultiplication('scaleX(2)', 'scale(1, 2)');
assertNoPostMultiplication('scaleX(2)', 'scale3d(1, 2, 3)');
assertNoPostMultiplication('skewY(10deg)', 'skewY(15deg)');
assertNoPostMultiplication('skew(10deg, 0)', 'skew(10deg, 0)');
assertNoPostMultiplication('rotate(10deg)', 'rotate(90deg)');
assertNoPostMultiplication('rotateY(10deg)', 'rotateY(90deg)');
assertNoPostMultiplication('rotateX(90deg)', 'rotateY(90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(1, 2, 1, 90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotateZ(90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 1, 90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 2, 90deg)');
assertNoPostMultiplication('rotateX(10deg)', 'rotate3d(1, 0, 0, 100deg)');
assertNoPostMultiplication('perspective(10px)', 'perspective(100px)');
assertNoPostMultiplication('matrix(1, 0, 0, 1, 0, 0)', 'matrix(2, 0, 0, 2, 0, 0)');
</script>
